﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Globalization;
using BneyBaruch.Ingest.MorningLesson.Services.Data;

namespace BneyBaruch.Ingest.Utils.FileSystem
{
    public static class DriveUtil
    {
        /// <summary>
        ///     Check drive free space.
        /// </summary>
        /// <param name="driveName">Drive name to check free space.</param>
        /// <param name="minimumSpace">Minimum space in bytes.</param>
        /// <returns></returns>
        public static bool IsEnoughSpace(string driveName, long minimumSpace)
        {
            if (string.IsNullOrEmpty(driveName))
                throw new ArgumentNullException("driveName");

            string formattedDriveName = driveName + @":\";

            DriveInfo[] drives = DriveInfo.GetDrives();

            DriveInfo drive = drives.FirstOrDefault(
                delegate(DriveInfo innerDrive)
                {
                    return string.Compare(innerDrive.Name, formattedDriveName, true) == 0;
                }
            );

            if (drive == null)
                throw new DriveNotFoundException(string.Format("Drive {0} could not be found.", formattedDriveName));

            if( !drive.IsReady)
                throw new DriveNotFoundException(string.Format("Drive {0} is not ready.", formattedDriveName));

            return drive.TotalFreeSpace > minimumSpace;
        }

        /// <summary>
        /// Calculate directory size including subdirectories and all files inside.
        /// </summary>
        /// <param name="directory">Directory info to calculate it size.</param>
        /// <returns></returns>
        public static long CalculateDirectorySize(DirectoryInfo directory)
        {
            long size = 0;
            // Add file sizes.
            FileInfo[] fis = directory.GetFiles();
            foreach (FileInfo fi in fis)
            {
                size += fi.Length;
            }
            // Add subdirectory sizes.
            DirectoryInfo[] dis = directory.GetDirectories();
            foreach (DirectoryInfo di in dis)
            {
                size += CalculateDirectorySize(di);
            }
            return (size);
        }

        /// <summary>
        /// Clean drive space if needed.Check if drive have enough free space,
        ///     if not - clean yyyy-mm-dd format older directories until reach minimum required free space.
        /// </summary>
        /// <param name="driveName">Drive name to check free space.</param>
        /// <param name="minimumSpace">Minimum space in bytes.</param>
        /// <returns></returns>
        public static FileSystemCleanData CleanFileSystemIfNeeded(string driveName, long minimumSpace, string folderToClean)
        {
            if (string.IsNullOrEmpty(driveName))
                throw new ArgumentNullException("driveName");

            if (string.IsNullOrEmpty(folderToClean))
                throw new ArgumentNullException("folderToClean");
            
            // TODO : Uncomments following lines in production.
            //if (!Directory.Exists(folderToClean))
            //    throw new DirectoryNotFoundException(string.Format("Folder {0} not found.", folderToClean));

            FileSystemCleanData fileSystemCleanData = new FileSystemCleanData();
            fileSystemCleanData.IsCleaned = false;
            fileSystemCleanData.FreedItems = new List<FileSystemCleanItem>();

            while (!IsEnoughSpace(driveName, minimumSpace))
            {
                FileSystemCleanItem fileSystemCleanItem = DeleteOlderDirectory(folderToClean);
                if (fileSystemCleanItem != null)
                {
                    fileSystemCleanData.FreedItems.Add(fileSystemCleanItem);
                    fileSystemCleanData.IsCleaned = true;
                }
            }

            return fileSystemCleanData;
        }

        #region Private Helper Methods

        private static FileSystemCleanItem DeleteOlderDirectory(string directoryPath)
        {
            // Try get all directories in ????-??-?? format, like our naming convention yyyy-mm-dd.
            string[] directories = Directory.GetDirectories(directoryPath, "????-??-??");
            SortedList<DateTime,string> dates = new SortedList<DateTime, string>();
            foreach (string directory in directories)
            {
                DateTime date;
                DirectoryInfo dir = new DirectoryInfo(directory);
                
                if (DateTime.TryParseExact(dir.Name, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out date))
                {
                    dates.Add(date, dir.FullName);
                }
            }
            if (dates.Count > 0)
            {
                FileSystemCleanItem fileSystemCleanItem = new FileSystemCleanItem();
    
                KeyValuePair<DateTime, string> olderDirectoryEntry = dates.First();
                DirectoryInfo dir = new DirectoryInfo(olderDirectoryEntry.Value);
                fileSystemCleanItem.FolderPath = olderDirectoryEntry.Value;
                fileSystemCleanItem.FreedSpace = CalculateDirectorySize(dir);
                Directory.Delete(olderDirectoryEntry.Value, true);
            }

            return null;
        }

        #endregion
    }
}
